package dmwr.repo.gae;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.CompositeFilter;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;

import dmwr.Dmwr;
import dmwr.bean.Application;
import dmwr.bean.Player;
import dmwr.repo.PlayerRepo;

public class PlayerRepoGae implements PlayerRepo {
	private static final String KIND = "Player";
	private static final String PROPERTY_USERNAME = "username";
	private static final String PROPERTY_POOL_ID = "poolId";
	private static final String PROPERTY_POSITION = "position";
	private static final String PROPERTY_CREATED = "created";

	@Override
	public Player byId(Long id) {
		return byKey(KeyFactory.createKey(KIND, id));
	}

	@Override
	public Player byUsernameAndPoolId(String username, Long poolId) {
		Collection<Filter> subFilters = new ArrayList<Query.Filter>();
		subFilters.add(FilterOperator.EQUAL.of(PROPERTY_USERNAME, username));
		subFilters.add(FilterOperator.EQUAL.of(PROPERTY_POOL_ID, poolId));
		return player(new Query(KIND).setFilter(
				new CompositeFilter(CompositeFilterOperator.AND, subFilters))
				.addSort(PROPERTY_CREATED, Query.SortDirection.ASCENDING));
	}

	@Override
	public void save(Player player) {
		Entity entity;
		if (player.getId() == null) {
			entity = new Entity(KIND);
			entity.setProperty(PROPERTY_USERNAME, player.getUsername());
			entity.setProperty(PROPERTY_POOL_ID, player.getPoolId());
			entity.setProperty(PROPERTY_CREATED, System.currentTimeMillis());
		} else {
			Key key = KeyFactory.createKey(KIND, player.getId());
			try {
				entity = Gae.datastoreService().get(key);
			} catch (EntityNotFoundException e) {
				throw new RuntimeException(e);
			}
		}
		entity.setProperty(PROPERTY_POSITION, player.getPosition());
		Gae.datastoreService().put(entity);
		if (player.getId() == null) {
			player.setId(entity.getKey().getId());
		}
	}

	@Override
	public void deleteAll() {
		Query query = new Query(KIND).addSort(PROPERTY_CREATED,
				Query.SortDirection.ASCENDING);
		List<Entity> results;
		while (!(results = Gae.datastoreService().prepare(query)
				.asList(FetchOptions.Builder.withLimit(100))).isEmpty()) {
			for (Entity result : results) {
				Gae.datastoreService().delete(result.getKey());
			}
		}
		Application application = Dmwr.applicationRepo().get();
		application.setEntryCount(0L);
		Dmwr.applicationRepo().save(application);
	}

	private Player byKey(Key key) {
		try {
			return player(Gae.datastoreService().get(key));
		} catch (EntityNotFoundException e) {
			return null;
		}
	}

	private Player player(Query query) {
		List<Entity> results = Gae.datastoreService().prepare(query)
				.asList(FetchOptions.Builder.withLimit(1));
		Entity result = !results.isEmpty() ? results.get(0) : null;
		return player(result);
	}

	private Player player(Entity result) {
		return result != null ? new Player(result.getKey().getId(),
				(String) result.getProperty(PROPERTY_USERNAME),
				(Long) result.getProperty(PROPERTY_POOL_ID),
				(Long) result.getProperty(PROPERTY_POSITION)) : null;
	}
}
